package drds.server.buffer;

import java.nio.ByteBuffer;
import java.util.BitSet;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 
 * @author 曹正辉<330937205@qq.com>
 * 
 */
public class Page {

	private final ByteBuffer byteBuffer;
	private final int blockSize;
	private final int blockNum;
	private final BitSet blockBitSet;
	private final AtomicBoolean lock = new AtomicBoolean(false);

	/**
	 * 
	 * @param byteBuffer
	 * @param blockSize
	 *            块的大小
	 */
	public Page(ByteBuffer byteBuffer, int blockSize) {
		super();
		this.byteBuffer = byteBuffer;
		this.blockSize = blockSize;
		this.blockNum = this.byteBuffer.capacity() / this.blockSize;
		this.blockBitSet = new BitSet(blockNum);

	}

	/**
	 * 创建子ByteBuffer
	 */
	public ByteBuffer allocateSubByteBuffer(int blockCount) {
		/**
		 * 请求资源锁,允许失败
		 */
		if (!this.lock.compareAndSet(false, true)) {
			return null;
		}
		int blockStartIndex = -1;
		int count = 0;
		try {
			for (int i = 0; i < this.blockNum; i++) {
				// not used
				if (this.blockBitSet.get(i) == false) {
					// not started
					if (blockStartIndex == -1) {
						blockStartIndex = i;
						count = 1;
						// 如果只是分配一个数据块则结束
						if (blockCount == 1) {
							break;
						}
					} else {
						// 分配成功
						if (++count == blockCount) {
							break;
						}
					}
				} else {
					// 分配物理内存上连续资源,如果遇到已经分配的则之前的PASS掉。
					blockStartIndex = -1;
					count = 0;
				}
			}
			if (count == blockCount) {
				int start = blockStartIndex * this.blockSize;
				int end = start + blockCount * this.blockSize;
				this.byteBuffer.limit(end);
				this.byteBuffer.position(start);
				ByteBuffer subByteBuffer = this.byteBuffer.slice();
				markBlocksUsed(blockStartIndex, blockCount);
				return subByteBuffer;
			} else {
				return null;
			}
		} finally {
			lock.set(false);
		}
	}

	/**
	 * 内存使用情况标记为已经使用
	 */
	private void markBlocksUsed(int blockStartIndex, int blockCount) {
		for (int i = 0; i < blockCount; i++) {
			blockBitSet.set(blockStartIndex + i);
		}
	}

	/**
	 * 内存使用情况标记为未使用
	 */
	private void markBlocksUnUsed(int blockStartIndex, int blockCount) {
		for (int i = 0; i < blockCount; i++) {
			blockBitSet.clear(blockStartIndex + i);
		}
	}

	public boolean recycleSubByteBuffer(//
			ByteBuffer byteBuffer,//
			int blockStartIndex,//
			int blockCount) {//

		if (byteBuffer == this.byteBuffer) {

			while (!this.lock.compareAndSet(false, true)) {
				Thread.yield();
			}
			// util get lock
			try {
				markBlocksUnUsed(blockStartIndex, blockCount);
			} finally {
				lock.set(false);
			}
			return true;
		}
		return false;
	}
}
